<?php
/**
 * Documentation, License etc.
 *
 * @package SimpleFlightStats
 */

/** 
Idee che stanno alla base di tutto:
- i dati devono venire letti 1 sola volta
- la classe deve conoscere, all'inizio delle elaborazioni, quali funzioni e con quali parametri verranno chiamate
NO - deve esserci una funzione "madre" che a fronte di una lettura di dati da database va a chiamare le varie funzioni
- ogni funzione puo' contenere delle proprie variabili, che verranno utilizzate durante la creazione delle statistiche
- al termine della lettura dei dati del database, ogni funzione deve avere una funzione di "commit" che di fatto e' 
quella che elabora tutto (potrebbe essere {func}_calc)
- a pensarci, forse la cosa migliore e' leggere tutto in un array, che poi verra' elaborato da tutte le varie funzioni
---> mi sfugge se sia meglio chiarare n volte una funzione o caricare tutto l'array ... credo la seconda, anche se spreco
un po' piu' RAM
- al termine, ogni funzione restituira' il proprio risultato, che potra' essere uno scalare, un array, ecc.ecc.
**/

define('LATITUDE','latitude');
define('LONGITUDE','longitude');
define('ALTITUDE','altitude');
define('TIMESTAMP','timestamp');
    
class simpleflightstats {
    public $functions=array();
    
    public $data=array(); /* questo array contiene latitude, longitude, altitude, timestamp */
		       /* i dati devono arrivare gia' ordinati per tempo, oppure devo ordinare io l'array */
    public $counter = 0; /* contatore dell'array , per assicurarmi poi di leggere i dati nell'ordine voluto */

    public $maxAltitude_results = 0;
    public $maxElevation_results = 0;
    public $variation_results = array('ascending'=>0,'descending'=>0);
    
    // harvesine
    private $earth_radius = array('km' => 6371);
    private $max_altitude = -99999;
    private $min_altitude = 99999;

/**
 Funzione che va a chiamare tutte quelle che sono definite
**/

  public function mother_calc () {
    $result=array();
    foreach ( $this->functions as $func_name) {
	$func_call = $func_name . "_calc";
	$result[$func_name] = $this->$func_call();
    }
    return $result;
  }
  
  public function insert_data ($latitude,$longitude,$altitude,$timestamp) {
	$this->data[$this->counter][LATITUDE]=$latitude;
	$this->data[$this->counter][LONGITUDE]=$longitude;
	$this->data[$this->counter][ALTITUDE]=$altitude;
	$this->data[$this->counter][TIMESTAMP]=$timestamp;
	// echo $this->counter > 0 ? $this->data[$this->counter][TIMESTAMP] - $this->data[$this->counter - 1][TIMESTAMP]:0;
	// echo "\n";
	$this->counter++;
	// calcola il valore massimo mentre inserisce i dati
	$this->max_altitude=$altitude > $this->max_altitude ? $altitude:$this->max_altitude;
	$this->min_altitude=$altitude < $this->min_altitude ? $altitude:$this->min_altitude;
	
	return TRUE;
  }

// calcola il valore massimo
  private function max_altitude_elimina () {
	// $minimum=
	if ($this->max_altitude < 0) {
	  $this->max_altitude=$this->data[0][ALTITUDE];
	  for($i=0;$i<$this->counter;$i++) {
	    $this->max_altitude=$this->data[$i][ALTITUDE] > $this->max_altitude ? $this->data[$i][ALTITUDE]:$this->max_altitude;
	    // echo $this->max_altitude . ":" . $this->data[$i][ALTITUDE] ."\n";
	  //  $minimum=$minimum > $this->data[$i][ALTITUDE] ? $this->data[$i][ALTITUDE]:$minimum;
	  }
	}
	return $this->max_altitude;
  }

/**
 From wikipedia
**/
  private function distance_harvesine($latitude1, $longitude1, $latitude2, $longitude2) {  
     $earth_radius = 6371;  
       
     $dLat = deg2rad($latitude2 - $latitude1);  
     $dLon = deg2rad($longitude2 - $longitude1);  
       
     $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($latitude1)) * cos(deg2rad($latitude2)) * sin($dLon/2) * sin($dLon/2);  
     $c = 2 * asin(sqrt($a));  
     $d = $this->earth_radius['km'] * $c;  
       
     return $d;  
  }
  
  public function maxAltitude_calc () {
	$this->maxAltitude_results = $this->max_altitude;
	return $this->max_altitude;
  }

/**
 Ritorna l'elevazione massima. Al momento la calcolo come [massima altezza] - [altezza iniziale]
 In realta' andrebbe calcolata come la massima altezza raggiunta dopo la minima altezza
**/
  public function maxElevation_calc () {
	//$this->maxElevation_results = $this->max_altitude - $this->data[0][ALTITUDE];
	// echo "max_altitude: ".$this->max_altitude;
	// echo "data[0]: ".$this->data[0][ALTITUDE];
	return $this->max_altitude - $this->data[0][ALTITUDE];
  }
  
  public function variation_calc () {
	$previous=$this->data[0][ALTITUDE];
	for($i=1;$i<$this->counter;$i++) {
	  if ($this->data[$i][ALTITUDE] > $previous) {
	    // this->variation('ascending')
	    $this->variation_results['ascending'] += $this->data[$i][ALTITUDE] - $previous;
	  }
	  else {
	    // this->variation('descending') or equal
	    $this->variation_results['descending'] += $previous - $this->data[$i][ALTITUDE];
	  }
	  $previous=$this->data[$i][ALTITUDE];
	}
	return $this->variation_results;
  }
  
  private function secondsToWords($seconds)
  {
    /*** return value ***/
    $ret = "";

    /*** get the hours ***/
    $hours = intval(intval($seconds) / 3600);
    // if($hours > 0)
    // {
        $ret .= str_pad($hours,2,"0") . ":";
    // }
    /*** get the minutes ***/
    $minutes = bcmod((intval($seconds) / 60),60);
    // if($hours > 0 || $minutes > 0)
    // {
        $ret .= str_pad($minutes,2,"0") . ":";
    // }
  
    /*** get the seconds ***/
    $seconds = bcmod(intval($seconds),60);
    $ret .= str_pad($seconds,2,"0");

    return $ret;
  }
  
  public function start_end_time_calc() {
      return array('start_time' => date ("d-m-Y H:i:s",$this->data[0][TIMESTAMP]), 'end_time' => date ("d-m-Y H:i:s",$this->data[$this->counter - 1][TIMESTAMP]), 'running_time' => $this->secondsToWords($this->data[$this->counter - 1][TIMESTAMP] - $this->data[0][TIMESTAMP]));
  }

  public function distance_2d_calc() {
    /** 
     Calcola la distanza percorsa senza tener conto dell'altezza
    **/
    $distance=0;
    for ($i=1; $i < $this->counter; $i++) {
      $distance += $this->distance_harvesine($this->data[$i][LATITUDE],$this->data[$i][LONGITUDE],$this->data[$i-1][LATITUDE],$this->data[$i-1][LONGITUDE]);
    }
    return $distance; // in km
  }

/** 
utilizzo la harvesine per la velocita' sferica, ma per l'altitudine utilizzo la velocita' normale
**/

  public function speed_calc () {
    $results=array();
    bcscale(10);
    $count=0;
    $speed_sum=0;
    $speed_alt_sum=0;
    $speed_altitude=0;
    $top_speed=0;
    $top_altitude_speed=0;
    $timediff=0;
    $time_sum=0;
    for ($i=1; $i < $this->counter; $i++) {
      $timediff=$this->data[$i][TIMESTAMP]-$this->data[$i-1][TIMESTAMP];
      $distance = $this->distance_harvesine($this->data[$i][LATITUDE],$this->data[$i][LONGITUDE],$this->data[$i-1][LATITUDE],$this->data[$i-1][LONGITUDE]);

      $speed_altitude=bcmul(bcdiv($this->data[$i][ALTITUDE]-$this->data[$i-1][ALTITUDE],$timediff),3.6); // speed m/s
      if ( bccomp($speed_altitude,0) == -1 ) {
	$speed_altitude = bcmul($speed_altitude,-1);
      }
      
      $speed = bcmul(bcdiv($distance,$timediff),3600);
      
      $top_speed = bccomp($top_speed,$speed) == -1  ? $speed : $top_speed;
      $top_altitude_speed = bccomp($top_altitude_speed,$speed_altitude) == -1 ? $speed_altitude : $top_altitude_speed;
      
      $results[]=array('speed' => $speed,'speed_alt' =>$speed_altitude);

      $speed_sum = bcadd($speed_sum,bcmul($speed,$timediff));
      $speed_alt_sum = bcadd($speed_alt_sum,bcmul($speed_altitude,$timediff));
      $time_sum += $timediff;
      $count++;
    }
    return array('speed' => $results, 'average_speed' => bcdiv($speed_sum,$time_sum), 'average_speed_altitude' => bcdiv($speed_alt_sum,$time_sum), 'top_speed' => $top_speed, 'top_altitude_speed' => $top_altitude_speed);
  }
}